package com.ctvit.cch.core.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;

/**
 * 聚合分数计算工具类
 * <p>
 * 用于计算热点记录的聚合评分分数，综合考虑以下因素：
 * 1. 归一化原始分数
 * 2. 排名权重分
 * 3. 时间衰减因子
 * 4. 维度加分（传播广度、影响深度、创作价值）
 * 
 * @author heyingcheng
 * @date 2025/11/14
 */
public class AggregateScoreCalculatorUtils {

    // 排名基础分
    private static final double RANK_WEIGHT_BASE = 100.0;

    // 维度分数权重（总满分为10分）
    // 传播广度：衡量内容覆盖范围，权重4分
    // 影响深度：衡量用户参与度，权重3分
    // 创作价值：衡量内容质量，权重3分
    private static final double SPREAD_WIDE_WEIGHT = 4.0;
    private static final double INFLUENCE_DEPTH_WEIGHT = 3.0;
    private static final double CREATIVE_VALUE_WEIGHT = 3.0;

    // 前三名额外奖励
    private static final double FIRST_RANK_BONUS = 20.0;
    private static final double SECOND_RANK_BONUS = 15.0;
    private static final double THIRD_RANK_BONUS = 10.0;

    /**
     * 计算单条记录的聚合评分
     * 
     * @param platformWeight       平台权重系数
     * @param maxRawScore          最大原始分数（用于归一化）
     * @param recordRawScore       记录原始分数
     * @param recordRank           记录排名（从1开始）
     * @param recordUpdateTime     记录更新时间
     * @param recordSpreadWide     记录传播广度（0-1之间）
     * @param recordInfluenceDepth 记录影响深度（0-1之间）
     * @param recordCreativeValue  记录创作价值（0-1之间）
     * @return 聚合评分分数（保留两位小数）
     */
    public static double calculateAggregateScore(BigDecimal platformWeight, Long maxRawScore, Long recordRawScore,
            int recordRank, LocalDateTime recordUpdateTime, BigDecimal recordSpreadWide,
            BigDecimal recordInfluenceDepth, BigDecimal recordCreativeValue) {

        // 参数验证
        if (platformWeight == null || maxRawScore == null || recordRawScore == null || recordUpdateTime == null
                || recordSpreadWide == null || recordInfluenceDepth == null || recordCreativeValue == null) {
            return 0.0;
        }

        // 确保平台权重为正数
        if (platformWeight.compareTo(BigDecimal.ZERO) <= 0) {
            return 0.0;
        }

        // 确保最大原始分数为正数
        if (maxRawScore <= 0) {
            return 0.0;
        }

        // 确保排名为正数
        if (recordRank <= 0) {
            return 0.0;
        }

        // 1. 归一化原始score (0-100分)
        double normalizedScore = normalizeRawScore(recordRawScore, maxRawScore);

        // 2. 计算排名权重分 (0-120分)
        double rankScore = calculateRankScore(recordRank);

        // 3. 计算时间衰减因子 (0-1之间)
        double decayFactor = TimeDecayUtils.calculateDecayFactor(recordUpdateTime);

        // 4. 计算维度加分（传播广度、影响深度等） (0-10分)
        double dimensionBonus = calculateDimensionBonus(recordSpreadWide, recordInfluenceDepth, recordCreativeValue);

        // 5. 最终聚合评分计算 (0-230分) 
        // 公式: (归一化原始分×平台权重 + 排名分) × 时间衰减 + 维度加分
        double aggregateScore = (normalizedScore * platformWeight.doubleValue() + rankScore) * decayFactor
                + dimensionBonus;

        // 保留2位小数（使用BigDecimal确保精度）
        return new BigDecimal(aggregateScore).setScale(2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 归一化原始热度分（Min-Max归一化）
     * <p>
     * 
     * @param rawScore 原始分数
     * @param maxScore 最大原始分数
     * @return 归一化后的分数（0-100分）
     */
    private static double normalizeRawScore(long rawScore, long maxScore) {
        // 参数验证
        if (maxScore <= 0 || rawScore < 0) {
            return 0;
        }

        // 使用对数缩放处理极端值
        double logScore = Math.log10(rawScore + 1);
        double logMax = Math.log10(maxScore + 1);

        // 避免除以零的情况
        if (logMax == 0) {
            return 0;
        }

        // 计算归一化分数，并确保不超过100分上限
        double normalizedScore = (logScore / logMax) * 100.0;
        // 当rawScore大于maxScore时，确保归一化分数不超过100
        return Math.min(normalizedScore, 100.0);
    }

    /**
     * 排名权重计算
     * <p>
     * 排名越靠前，分数越高，前3名有额外加成
     * - 最小值: 当 rank ≤ 0 时，返回 0
     * - 最大值: 当 rank = 1 时，计算得 100.0/1 + 20.0 = 120.0
     * - 中间值示例:
     * - rank=2: 100.0/2 + 15.0 = 65.0
     * - rank=3: 100.0/3 + 10.0 ≈ 43.33
     * - rank=4: 100.0/4 = 25.0
     * - rank=10: 100.0/10 = 10.0
     * - rank=100: 100.0/100 = 1.0
     * - rank→∞: 100.0/rank → 0
     * 
     * @param rank 排名（从1开始）
     * @return 排名权重分
     */
    private static double calculateRankScore(int rank) {
        if (rank <= 0) {
            return 0;
        }

        // 基础分：排名越靠前，分数越高
        double baseScore = RANK_WEIGHT_BASE / rank;

        // 前三名额外奖励
        if (rank == 1) {
            baseScore += FIRST_RANK_BONUS;
        } else if (rank == 2) {
            baseScore += SECOND_RANK_BONUS;
        } else if (rank == 3) {
            baseScore += THIRD_RANK_BONUS;
        }

        return baseScore;
    }

    /**
     * 维度加分计算
     * <p>
     * 综合考虑传播广度、影响深度和创作价值三个维度，总满分为10分
     * - 传播广度：4分（衡量内容覆盖范围）
     * - 影响深度：3分（衡量用户参与度）
     * - 创作价值：3分（衡量内容质量）
     * 
     * 取值范围是 0.0 到 10.0 之间，
     * 其中 0.0 表示没有维度加分，
     * 10.0 表示所有维度都获得满分。
     * 
     * @param recordSpreadWide     记录传播广度（1-5之间）
     * @param recordInfluenceDepth 记录影响深度（1-5之间）
     * @param recordCreativeValue  记录创作价值（1-5之间）
     * @return 维度加分总和（0-10分）
     */
    private static double calculateDimensionBonus(BigDecimal recordSpreadWide, BigDecimal recordInfluenceDepth,
            BigDecimal recordCreativeValue) {
        double bonus = 0.0;

        // 传播广度加分（0-4分）- 将1-5映射到0-1范围
        if (recordSpreadWide != null) {
            double spreadWide = normalizeDimensionValue(recordSpreadWide.doubleValue());
            bonus += spreadWide * SPREAD_WIDE_WEIGHT;
        }

        // 影响深度加分（0-3分）- 将1-5映射到0-1范围
        if (recordInfluenceDepth != null) {
            double influenceDepth = normalizeDimensionValue(recordInfluenceDepth.doubleValue());
            bonus += influenceDepth * INFLUENCE_DEPTH_WEIGHT;
        }

        // 创作价值加分（0-3分）- 将1-5映射到0-1范围
        if (recordCreativeValue != null) {
            double creativeValue = normalizeDimensionValue(recordCreativeValue.doubleValue());
            bonus += creativeValue * CREATIVE_VALUE_WEIGHT;
        }

        return bonus;
    }

    /**
     * 将1-5范围的维度值归一化到0-1范围
     * 
     * @param value 1-5范围的维度值
     * @return 归一化后的0-1范围值
     */
    private static double normalizeDimensionValue(double value) {
        // 将1-5映射到0-1
        double normalized = (value - 1.0) / (5.0 - 1.0);
        // 确保结果在0-1范围内
        return Math.max(0.0, Math.min(1.0, normalized));
    }

}
